package network;

import gamemngr.GameMngr;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.Vector;
import java.util.concurrent.atomic.AtomicInteger;

import network.InformClients;

import Database.DBActions;
import Database.DBException;
import Database.PlayerPlayedGames;
import Database.UserInfo;

public class NewConnection extends Thread {

	private ObjectInputStream ois;
	private ObjectOutputStream oos;
	private Socket socket;
	private NetworkPlayer player;
	private static AtomicInteger numOfGames = new AtomicInteger(0);
	private DBActions dbact;

	public NewConnection(NetworkPlayer player, DBActions dbact){
		this.socket = player.getSocket();
		this.player = player;
		this.dbact = dbact;
	}

	public void run(){
		sendMsg(new OpenedGamesMsg(Server.openGames));
		while(true){
			NetworkMsg msg = getMsg();
			if(msg == null){ // client disconnected
				for(int index : Server.openGames.keySet()){
					NetworkGame game = Server.openGames.get(index);
					game.removePlayer(player);
					if(game.getPlayers().size() == 0){
						Server.openGames.remove(index);
						Server.players.sendOpenGamesToUnassigned();
					}
				}
				return;
			}
			switch(msg.getType()){
				case JoinGame:
					NetworkGame game = Server.openGames.get(msg.getGameIndex());
					if(game == null){
						sendMsg(new ErrorMsg("The game was closed."));
					} else {
						player.setName(msg.getName());
						game.addPlayer(player);
						Server.players.remove(player);
						sendMsg(new InfoMsg("You have successfully joined the game"));
						if (game.numberOfPlayers() == game.getWantedPlayers()){
							runNetworkGame(game.getGame());
							game.sendStartGame();
						}
						Server.players.sendOpenGamesToUnassigned();
					}
					break;
				case CreateNewGame:
					OpenGameMsg m = (OpenGameMsg)msg;
					int gameID = numOfGames.getAndIncrement();
					player.setName(m.getName());
					Server.openGames.put(gameID, new NetworkGame(player, m.getNumOfPlayers(), gameID));
					sendMsg(new InfoMsg("You are now waiting for other players to join..."));
					Server.players.remove(player);
					Server.players.sendOpenGamesToUnassigned();
					break;
				case InformAll:
					InformClients ic = (InformClients)msg;
					int id = ic.getGame().getGameID();
					Server.closedGames.get(id).informAll(new GameMngrMsg(ic.getGame(), ic.getBriefDo()));
					break;
				case Disconnection:
					DisconnectionMsg disconnect = (DisconnectionMsg)msg;
					NetworkGame gameToClose = Server.closedGames.get(disconnect.getGameId());
					//TODO handle illegal game number
					if (gameToClose!=null)
						gameToClose.informAll(disconnect);
					Server.closedGames.remove(disconnect.getGameId());
					break;
				case Winning:
					WinniningMsg winning = (WinniningMsg)msg;
					NetworkGame gameToInform = Server.closedGames.get(winning.getGameId());
					gameToInform.informAll(new YouLost());
					try {
						updateDB(gameToInform, player.getID());
					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					break;
				case AddNewUser:
					AddNewUserMsg register = (AddNewUserMsg)msg;
					try {
						dbact.AddUser(register.getUsername(), register.getPassword(), register.getEmail());
						sendMsg(new DBInfoMsg("Success(dummy)"));
					} catch (DBException e) {
						sendMsg(new ErrorMsg(e.getMessage()));
					} catch (SQLException e) {
						sendMsg(new ErrorMsg("Server error. Call the system!"));
					} catch (ClassNotFoundException e) {
						sendMsg(new ErrorMsg("Server error. Call the system!"));
					}
					break;
				case AuthenticateUser:
					AuthenticateUserMsg login = (AuthenticateUserMsg)msg;
					try {
						UserInfo userInfo = dbact.AuthenticateUser(login.getUsername(), login.getPassword());
						if (userInfo!=null)
							sendMsg(new DBInfoMsg("Success(dummy)"));
						else
							sendMsg(new ErrorMsg("Server error. Call the system!"));
					} catch (SQLException e) {
						sendMsg(new ErrorMsg("Server error. Call the system!"));
					} catch (ClassNotFoundException e) {
						sendMsg(new ErrorMsg("Server error. Call the system!"));
					} catch (DBException e) {
						sendMsg(new ErrorMsg(e.getMessage()));
					}
					break;
				case UserProperties:
					String username = ((UserPropertiesMsg)msg).getUsername();
					try {
						DBInfoMsg userPropertiesMsg = new DBInfoMsg(dbact.getUserProperties(username));
						sendMsg(userPropertiesMsg);
					} catch (ClassNotFoundException e) {
						sendMsg(new ErrorMsg("Server error. Call the system!"));
					} catch (SQLException e) {
						sendMsg(new ErrorMsg("Server error. Call the system!"));
					}
					break;
				case GameHistory:
					String user = ((GameHistoryMsg)msg).getUsername();
					try {
						DBInfoMsg userHistoryMsg = new DBInfoMsg(dbact.getUserPlayedGamesString(user));
						sendMsg(userHistoryMsg);
					} catch (ClassNotFoundException e) {
						sendMsg(new ErrorMsg("Server error. Call the system!"));
					} catch (SQLException e) {
						sendMsg(new ErrorMsg("Server error. Call the system!"));
					}
					break;
				case RecordsTable:
					DBInfoMsg recordsMsg;
					try {
						recordsMsg = new DBInfoMsg(dbact.getRecordTableString());
						sendMsg(recordsMsg);
					} catch (DBException e) {
						sendMsg(new ErrorMsg(e.getMessage()));
					} catch (SQLException e) {
						sendMsg(new ErrorMsg("Server error. Call the system!"));
					} catch (ClassNotFoundException e) {
						sendMsg(new ErrorMsg("Server error. Call the system!"));
					}
												
				
			}
		}
	}

	private void updateDB(NetworkGame game, int winnerID) throws ClassNotFoundException, SQLException {
		Vector<PlayerPlayedGames> vector = new Vector<PlayerPlayedGames>();
		for(NetworkPlayer player : game.getPlayers()){
			if(player.getID() == winnerID)
				vector.add(new PlayerPlayedGames(player, true));
			else
				vector.add(new PlayerPlayedGames(player, false));
		}
		DBActions.AddGame(vector);
	}

	private boolean sendMsg(NetworkMsg msg){
		try {
			oos = new ObjectOutputStream(socket.getOutputStream());
			oos.writeObject(msg);
		} catch (IOException e) {
			return false;
		}
		return true;
	}
	
	/**
	 * @return the message
	 */
	private NetworkMsg getMsg() {
		NetworkMsg msg = null;
		try {
			ois = new ObjectInputStream(socket.getInputStream());
			msg = (NetworkMsg)ois.readObject();
		} catch (IOException e) {
			//e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		return msg;
	}
	
	/**
	 * init all the game logic
	 * @param game
	 */
	private void runNetworkGame(GameMngr game) {
		game.status = GameMngr.GameStatus.DIVIDE_SOLDIERS;
		game.setNumMoveRule(-1);
		game.setMaxReenforcement(Integer.MAX_VALUE);
		game.setLimitMove(true);
		game.divideCountries();
		game.giveMissionsToPlayers();
	}
}
